﻿using System.Collections.ObjectModel;
using Microsoft.Xna.Framework;
using ShevaEngine.Core.GameLogic;
using ShevaEngine.Core.Modules.Graphics;
using ShevaEngine.Core.Modules.Graphics.Cameras;
using ShevaEngine.Core.Modules.Graphics.Lights;

namespace ShevaEngine.Core.Modules.Scenes
{
    /// <summary>
    /// Scene class.
    /// </summary>
    public class Scene
    {        
        /// <summary>Scene objects.</summary>
        private ObservableCollection<SceneObject> SceneObjects;
        /// <summary>Lights.</summary>
        public ObservableCollection<Light> Lights;
        /// <summary>Rendering pipeline.</summary>
        private RenderingPipeline RenderingPipeline;
        

        /// <summary>
        /// Constructor.
        /// </summary>
        public Scene()
        {
            this.SceneObjects = new ObservableCollection<SceneObject>();
            this.Lights = new ObservableCollection<Light>();
            this.RenderingPipeline = new RenderingPipeline();            
        }

        /// <summary>
        /// Method returns rendering pipeline.
        /// </summary>
        /// <param name="camera"></param>
        /// <returns></returns>
        public virtual RenderingPipeline GetRenderingPipeline(Camera camera)
        {
            this.RenderingPipeline.Clear();

            foreach (SceneObject sceneObject in this.SceneObjects)
                sceneObject.AddInRenderingPipeline(this.RenderingPipeline, camera);

            this.RenderingPipeline.Sort();

            return this.RenderingPipeline;
        }

        ///// <summary>
        ///// Methods flush scene in txt.
        ///// </summary>
        ///// <param name="filename"></param>
        //public void FlushScene(string filename)
        //{
        //    using (StreamWriter sw = new StreamWriter(filename))
        //    {
        //        foreach (SceneObject sceneObject in this.SceneObjects)
        //            if (sceneObject is GraphicsSceneObject)
        //                foreach (SceneNode item in (sceneObject as GraphicsSceneObject).Model)
        //                    item.WriteInTxt(sw, 0);
        //    }
        //}

        //RenderingPipeline pipeline = null;
        //DateTime lastDateTime = DateTime.Now;

        ///// <summary>
        ///// Method returns rendering pipeline.
        ///// </summary>
        ///// <param name="camera"></param>
        ///// <returns></returns>
        //public virtual RenderingPipeline GetRenderingPipeline(Camera camera)
        //{
        //    if (pipeline == null || DateTime.Now - lastDateTime > new TimeSpan(0, 0, 3))
        //    {
        //        pipeline = new RenderingPipeline();

        //        foreach (SceneObject sceneObject in this.SceneObjects)
        //            sceneObject.AddInRenderingPipeline(pipeline, camera);

        //        lastDateTime = DateTime.Now;

        //        pipeline.Sort();
        //    }

        //    return pipeline;
        //}

        /// <summary>
        /// Method returns rendering pipeline.
        /// </summary>
        /// <param name="camera"></param>
        /// <returns></returns>
        public virtual RenderingPipeline GetRenderingPipeline(Vector3 position, float distanceSquared)
        {
            this.RenderingPipeline.Clear();

            foreach (SceneObject sceneObject in this.SceneObjects)
                sceneObject.AddInRenderingPipeline(this.RenderingPipeline, position, distanceSquared);

            return this.RenderingPipeline;
        }        

        /// <summary>
        /// Method adds scene object in scene.
        /// </summary>
        /// <param name="sceneObject"></param>
        public virtual void AddObjectInScene(SceneObject sceneObject)
        {
            if (sceneObject is Light)
                this.Lights.Add(sceneObject as Light);
            else
                this.SceneObjects.Add(sceneObject);            
        }

        /// <summary>
        /// Method removes scene object from scene.
        /// </summary>
        /// <param name="sceneObject"></param>
        public virtual void RemoveObjectFromScene(SceneObject sceneObject)
        {
            if (sceneObject is Light)
            {
                if (this.Lights.Contains(sceneObject as Light))
                    this.Lights.Add(sceneObject as Light);
            }
            else
                if (this.SceneObjects.Contains(sceneObject))
                    this.SceneObjects.Remove(sceneObject);            
        }               
    }

    /// <summary>
    /// On light changed callback.
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="light"></param>
    public delegate void OnLightChangedCallback(object sender, Light light);
}
